(function($) {
	
	// --- General --- {{
	
	/**
	 * @param object obj
	 * @return int
	 */
	function objectPropertyCount(obj) {
		var count = 0;
		for (prop in obj) {
			count++;
		}
		return count;
	}
	
	/**
	 * @param mixed needle
	 * @param array haystack
	 * @return bool
	 */
	function inArray(needle, haystack) {
		if (typeof(haystack) == "object") {
			for (var i=0; i < haystack.length; i++) {
				if (needle == haystack[i]) {
					return true;
				}
				
			}
		}		
		return false;
	}
	
	/**
	 * @param array data
	 * @param string selected
	 * @return string 
	 */
	function getSelectboxOptions(data, selected) {
		var view = '';
		var selectedHtml;
		if (typeof(data) == 'object') {
			for(var i=0; i < data.length; i++){
				selectedHtml = (selected == data[i]) ? 'selected="selected"' : '';
				view += '<option ' + selectedHtml + 'value="' + data[i] + '">' + data[i] + '&nbsp;</option>';
				
			}	
		}
		return view;
	} 

	/**
	 * Class
	 */
	function Arrow()
	{
		this.up = '<span class="ui-icon ui-icon-triangle-1-n pager-arrow pager-arrow-up"></span>';
		this.down = '<span class="ui-icon ui-icon-triangle-1-s pager-arrow pager-arrow-down"></span>';
		this.no = '<span class="ui-icon ui-icon-triangle-2-e-w pager-arrow pager-arrow-no"></span>';
		
		/**
		 * @return string
		 */
		this.getCurrent = function(sord) {
			if (sord == 'desc') {
				return this.up;	
			}
			if (sord == 'asc') {
				return this.down;	
			}
		};
		
	}
	var aArrow = new Arrow();
	
	// --- General --- }}
	
	Pager.defaultPagerName = 'default_pager_name';
	/**
	 * class 
	 */
	function Pager(initOptions){
		/**
		 * @object
		 */
		this.params = {};
			
		// --- Params --- {{
		
		/**
		 * @return void
		 */
		this.setDefaultParams = function() {
			this.params.name = Pager.defaultPagerName;
			this.params.pager = null;
			this.params.url = null;
			this.params.rowList = [10,20,50];
			this.params.rowNum = 10;
			this.params.colNames = null;
			this.params.colModel = null;
			this.params.sortname = 'id';
			this.params.sord = 'desc';
			this.params.primaryIndex = 'id';
			this.params.page = 1;
			this.params.sidx = null;
			this.params.paginator = {};
			this.params.simpleSearchClass = 'simple-search';
			this.params.emptyList = 'Empty list';
			this.params.actionsWidth = '8%';
			this.params.urlLink = null;
			this.params.simpleSearchClass = 'simple-search';
			this.params.dialogDeleteId = 'dialog-delete';
			this.params.dialogErrorId = 'dialog-error';
			this.params.loadingMessageClass = 'loading-message';
		}; 

		/**
		 * @param string columnName
		 * @return array
		 */
		this.getSidx = function(columnName) {
			//console.log(this.params);
			
			return (null != this.params.colModel[columnName]['sort']) ? 
				this.params.colModel[columnName]['sort'] : this.params.colModel[columnName]['index'];
		};
	
		/**
		 * @param object settings
		 * @return void
		 */
		this.setParams = function(settings) {
			this.setDefaultParams();
			if (settings) {
				$.extend(this.params, settings);
			}
			this.params.sord = this.params.sord.toLocaleLowerCase(); 
			
			this.params.sidx = this.getSidx(this.params.sortname); 
		};
		
		/**
		 * @param string name
		 * @return string
		 */
		this.getParam = function(name) {
			return this.params[name];
		};
	
		/**
		 * @return void
		 */
		this.inverseSord = function() {
			this.params.sord = (this.params.sord == 'asc') ? 'desc' : 'asc';
		};
		
		/**
		 * @param object data
		 * @return void
		 */
		this.paginatorInit = function(data) {
			this.params.paginator.first = data.first; //Номер первой страницы (т.е. 1)
			this.params.paginator.firstItemNumber = data.firstItemNumber; //Действительный номер первого элемента на текущей странице
			this.params.paginator.firstPageInRange = data.firstPageInRange; //Первая страница в ряде, возвращенном текущим стилем прокрутки
			this.params.paginator.current = data.current; //Текущий номер страницы
			this.params.paginator.currentItemCount = data.currentItemCount; //Количество элементов на текущей странице
			this.params.paginator.itemCountPerPage = data.itemCountPerPage; //Максимальное количество отображаемых на странице элементов
			this.params.paginator.last = data.last; //Номер последней страницы
			this.params.paginator.lastItemNumber = data.lastItemNumber; //Действительный номер последнего элемента на текущей странице
			this.params.paginator.lastPageInRange = data.lastPageInRange; //Последняя страница в ряде, возвращенном текущим стилем прокрутки
			this.params.paginator.next = data.next; //Номер следующей страницы
			this.params.paginator.pageCount = data.pageCount; //Общее количество страниц
			this.params.paginator.pagesInRange = data.pagesInRange; //Массив страниц, возвращенный текущим стилем прокрутки
			this.params.paginator.previous = data.previous; //Номер предыдущей страницы
			this.params.paginator.totalItemCount = data.totalItemCount; //Общее количество элементов
		};
		
		/**
		 * @param string name
		 * @return string
		 */
		this.getPaginatorParam = function(name) {
			return this.params.paginator[name];
		};
		
		// --- Params --- }}
	
		// --- View --- {{
	
		/**
		 * @return void
		 */
		this.rowDeleteConfirmed = function() {
			this.load({
				"ac": "delete_row", 
				"primary": this.rowDelete.primary
			});
		};
	
		/**
		 * @return void
		 */
		this.initViewHtml = function() {
			// --- delete dialog --- {{
			var dialog_delete_html = '<div id="' + this.params.dialogDeleteId + '" title="Подтверждение"><span>Удалить?</span></div>';
			var dialog_error_html = '<div id="' + this.params.dialogErrorId + '" title="Ошибка"><span style="color: red;">Не удается выполнить операцию.</span></div>';
			var processing_html = '<div class="ui-widget-overlay" style="display: none; width: 100%; height: 100%; z-index: 1001;"></div>' 
								+ '<img style="display: none; z-index: 1002;" class="processing" src="/css/jqueryui/cupertino/images/ui-anim_basic_16x16.gif" />';
								
			var html = dialog_delete_html + dialog_error_html + processing_html; 
			$('body').append(html);
			
			var that = this;
			$("#" + this.params.dialogDeleteId).dialog({
				modal: true,
				autoOpen: false,
				buttons: {
					Ok: function() {
						$(this).dialog('close');
						that.rowDeleteConfirmed();
					}
				} 
			});
			$("#" + this.params.dialogErrorId).dialog({ 
				autoOpen: false,
				buttons: {
					Ok: function() {
						$(this).dialog('close');
					}
				} 
			});
			// --- delete dialog --- }}
			
			// --- loading message --- {{
			var loadingHtml = '<div class="' + this.params.loadingMessageClass + '">Загрузка...</div>';
			//console.log(this.params.pager);
			$(this.params.pager).before(loadingHtml).hide().addClass("admin-pager-container");
			
			
			
			// --- loading message --- }}
		};
		
		/**
		 * @param object data
		 * @return string view
		 */
		this.getDataView = function(data) {
			var row = data.row;
			var view;
			var name, i, j;
			var index;
			var columnWidth;
	
			var currentArrow;
			
			if (data.totalItemCount == 0) {
				return this.params.emptyList;
			}
			
			view = '<table class="admin-pager">';
			/* --- navigation --- {{ */ 
			view += '<tr class="navigation-pager">';
			view += '<td class="pager-navigation" colspan="' 
				+ (objectPropertyCount(this.params.colNames)+1) 
				+ '">'
				
				+ '<div style="float: left;">View ' + this.params.paginator.firstItemNumber + '-' + this.params.paginator.lastItemNumber + ' of ' + this.params.paginator.totalItemCount + '</div>' 

				  + '<div style="float: right;" class="pager-navigation-controll">'
	
				  + '<select onchange="jQuery(\'' + this.params.pager + '\').mainPagerMethod(\''+this.params.name+'\' ,\'paginator_item_count_per_page\',jQuery(this).val())">' 
				  + getSelectboxOptions(this.params.rowList, this.params.paginator.itemCountPerPage) 
				  + '</select>&nbsp;&nbsp;&nbsp;&nbsp;'
				  
				  + '<a href="javascript: void jQuery(\'' + this.params.pager + '\').mainPagerMethod(\''+this.params.name+'\' ,\'paginator_first\')"><<</a></span>&nbsp;&nbsp;'
				  + '<a href="javascript: void jQuery(\'' + this.params.pager + '\').mainPagerMethod(\''+this.params.name+'\' ,\'paginator_previous\')"><</a></span>&nbsp;&nbsp;'
				  
				  + 'Page '
				  + '<input onkeyup="jQuery(\'' + this.params.pager + '\').mainPagerMethod(\''+this.params.name+'\' ,\'paginator_current\', jQuery(this).val(), event);" value="' + this.params.page + '"  autocomplete="off" />'
				  + ' of ' + this.params.paginator.pageCount + '&nbsp;&nbsp;'
				  
				  + '<a href="javascript: void jQuery(\'' + this.params.pager + '\').mainPagerMethod(\''+this.params.name+'\' ,\'paginator_next\')">></a>&nbsp;&nbsp;'
				  + '<a href="javascript: void jQuery(\'' + this.params.pager + '\').mainPagerMethod(\''+this.params.name+'\' ,\'paginator_last\')">>></a>'
				  
				  + '</div>'
				  
				  + '</td>';
			view += '</tr>';
			/* --- navigation --- }} */
			
			//console.log(aArrow);
			
			/* --- headers --- {{ */
			view += '<tr class="head-pager">';
			for (name in this.params.colNames) {
				currentArrow = aArrow.no;
				
				if ( this.params.sidx == this.getSidx(name) ) {
					currentArrow = aArrow.getCurrent(this.params.sord);
				}
				columnWidth = (this.params.colModel[name]['width'] != null) ? 'width="' + this.params.colModel[name]['width'] + '"' : '';
				view += '<td id="column_' + name + '"' + columnWidth + '><a href="javascript: void jQuery(\'' + this.params.pager + '\').mainPagerMethod(\''+this.params.name+'\' ,\'sort_column\', \'' + name + '\')">' + '<span style="float:left;">' + this.params.colNames[name] + '</span>'
				     + currentArrow                                                                                                                                                            		
	                 + '</a></td>';
			}
			view += '<td width="' + this.params.actionsWidth + '"></td>';
			view += '</tr>';
			/* --- headers --- }} */
			
			/* --- data rows --- {{ */
			var urlLinkHtmlOnmouseover = '';
			var urlLinkHtmlOnclick = '';
			if (this.params.urlLink != null) {
				urlLinkHtmlOnmouseover = 'onmouseover="this.style.cursor=\'pointer\'" ';
			}
			
			var indexObject = {};
			for (i in row) {
				view += '<tr class="general-row" onmouseover="jQuery(this).addClass(\'pager-row-active\');" onmouseout="jQuery(this).removeClass(\'pager-row-active\')">';
				if (this.params.urlLink != null) {
					urlLinkHtmlOnclick = ' onclick="window.location=\''+ this.params.urlLink +'/'+this.params.primaryIndex+'/'+row[i][this.params.primaryIndex]+'/\'" ';
				}	
				for (name in this.params.colModel) {
					indexObject = {};
					view += '<td '+urlLinkHtmlOnclick+urlLinkHtmlOnmouseover + ' >';
					if (null != this.params.colModel[name]['handler']) {
						for (j in this.params.colModel[name]['index']) {
							index = this.params.colModel[name]['index'][j];
							indexObject[index] = row[i][index];
						}	
						view += eval(this.params.colModel[name]['handler'] + "(indexObject)");
					} else {
						for (j in this.params.colModel[name]['index']) {
							index = this.params.colModel[name]['index'][j];
							view += row[i][index];
						}	
					}
					view += '</td>';
				}
				view += '<td class="pager_actions">';
					view += '<span class="ui-state-default"><a title="Edit" href="javascript: void jQuery(\'' + this.params.pager + '\').mainPagerMethod(\''+this.params.name+'\' ,\'row_edit\',' + row[i][this.params.primaryIndex] + ');" class="ui-icon ui-icon-pencil admin-pager-edit"></a></span>';
					view += '<span class="ui-state-default"><a title="Delete" href="javascript: void jQuery(\'' + this.params.pager + '\').mainPagerMethod(\''+this.params.name+'\' ,\'row_delete\',' + row[i][this.params.primaryIndex] + ');" class="ui-icon ui-icon-trash admin-pager-delete"></a></span>';
				view += '</td>';	
					
				view += '</tr>';
			}
			/* --- data rows --- }} */
			view += '</table>';
			
			return view;
		};
		
		// --- View --- }}
			
		// --- Ajax --- {{
		
		/**
		 * @param object ajaxParams
		 * @return void
		 */
		this.load = function(ajaxParams) {
			//console.log(this.params);
			
			var timer = null;
			var rows = (null != this.params.paginator.itemCountPerPage) ? 
					this.params.paginator.itemCountPerPage : this.params.rowNum;
			
			var loadParams = {
					"ac": "load", 
					"page": this.params.page,
					"rows": rows,
					"sidx": this.params.sidx,
					"sord": this.params.sord	
			}; 
			if (null != ajaxParams) {
				$.extend(loadParams, ajaxParams);
			}

			timer = setTimeout(function(){
				$(".ui-widget-overlay").show();
				$(".processing").show();
			}, 1000);
			
			var that = this;
			$.post(this.params.url, loadParams, function(data){
				
				//console.log(that.params);
				
				if (timer != null) {
					clearTimeout(timer);
					$(".ui-widget-overlay").hide();
					$(".processing").hide();
				} 
				
				if (data == false ) {
					$("#" + that.params.dialogErrorId).dialog('open');
				} else {
					that.params.page = data.current;
					
					that.paginatorInit(data);
					var view = that.getDataView(data);
					$('.' + that.params.loadingMessageClass).hide();
					
					$(that.params.pager).html(view);
					$(that.params.pager).show();
				}	
			});
		};
		
		/**
		 * @param int value
		 * @return void 
		 */
		this.paginatorItemCountPerPage = function(value) {
			this.params.paginator.itemCountPerPage = value;
			this.load({
				"ac": "set_item_count_per_page", 
				"value": value
			});
		};
		
		
		
		/**
		 * @param string primary
		 * @return void
		 */
		this.rowDelete = function(primary) {
			$("#" + this.params.dialogDeleteId).dialog('open');
			this.rowDelete.primary = primary;
		};
		
		/**
		 * @param int page
		 * @return void
		 */
		this.paginatorCurrent = function(page) {
			this.params.page = page;
			this.load({
				"ac": "set_current", 
				"page": page
			});
		};
		
		/**
		 * @param string colName
		 */
		this.sortColumn = function(colName) {
			if (!$(this.params.pager + '.head-pager td#column_' + colName + ' a span.pager-arrow').hasClass('pager-arrow-no') ) {
				this.inverseSord();
			}
			
			this.params.sortname = colName;
			this.params.sidx = this.getSidx(colName);
			
			$(this.params.pager + 'span.pager-arrow').remove();
			$(this.params.pager + 'tr.head-pager td a').append(aArrow.no);
			$(this.params.pager + '.head-pager td#column_' + colName + ' a span.pager-arrow').remove();
			var currentArrow = aArrow.getCurrent(this.params.sord);
			$(this.params.pager + '.head-pager td#column_' + colName + ' a').append(currentArrow);
				
			this.load();
		};
		
		/**
		 * @return void
		 */
		this.simpleSearch = function() {
			var timer = null;
			var searchString = null;
			var searchStringPrevious = null;
			var that = this;
			
			$(' .' + this.params.simpleSearchClass).keyup(function(){
				searchString = $(this).val();
				if (searchString == searchStringPrevious) { 
					return;
				}
				searchStringPrevious = searchString; 
				if (timer != null) {
					clearTimeout(timer);
					$(".ui-widget-overlay").hide();
					$(".processing").hide();
				} 
				timer = setTimeout(function(){
					$(".ui-widget-overlay").show();
					$(".processing").show();
					that.load({
						'ac' : 'simple_search',
						'search_string' : searchString  
					});
				}, 1000);
				  
			});
			
		};
		// --- Ajax --- }}
		
		// --- constructor --- {{
		this.setParams(initOptions);
		//console.log('here');
		this.initViewHtml();
		$('.' + this.params.loadingMessageClass).show();
		this.load();
		this.simpleSearch();
		// --- constructor --- }}
	}
	
	function pagerFactory(name, settings) {
		if (pagerFactory.instances[name] == null) {
			pagerFactory.instances[name] = new Pager(settings);	
		}
		
		return pagerFactory.instances[name];
	}
	pagerFactory.instances = {};
	
	
	// --- Public --- {{
	
	/**
	 * @param object setting
	 * @return object
	 */
	$.fn.mainPager = function(settings) {
		var name = (settings.name != null) ? settings.name : Pager.defaultPagerName; 
		var pager = pagerFactory(name, settings);
		return this;
	};
	
	/**
	 * @param string name
	 * @return object
	 */
	$.fn.mainPagerMethod = function(pagerName, name) {
		var pager = pagerFactory(pagerName);
		if (name == 'row_edit') {
			 var urlEdit = pager.getParam('urlEdit') + '/' + pager.getParam('primaryIndex') + '/' + arguments[2];
			 window.location = urlEdit;
		} 
		
		if (name == 'row_delete') {
			//console.log(arguments[2]);
			pager.rowDelete(arguments[2]);
		}	
		
		if (name == 'paginator_current') {
			var eventArg = arguments[3];
			if (eventArg.keyCode == 13) {
				pager.paginatorCurrent(arguments[2]);
			}
			eventArg.preventDefault();
		}
		
		if (inArray(name, ['paginator_first', 'paginator_previous', 'paginator_next', 'paginator_last'])) {
			var nameParts = name.split('_');
			var paginatorProp = nameParts[1];
			var newPage = pager.getPaginatorParam(paginatorProp);
			
			pager.paginatorCurrent(newPage);
		}
		
		if (name == 'paginator_item_count_per_page') {
			pager.paginatorItemCountPerPage(arguments[2]);
		}
		
		if (name == 'sort_column') {
			pager.sortColumn(arguments[2]);
		}

		return this;
	};
	
	// --- Public --- }}	
	
})(jQuery);